<?php
/**
 * Nordic Repository
 * 
 * LICENSE
 * 
 * The new BSD license is applied on this source-file. For further
 * information please visit http://license.nordic-dev.de/newbsd.txt
 * or send an email to andre.moelle@gmail.com.
 */

/**
 * Necessary includes.
 */
require_once 'Nordic/Pagebar/Exception.php';
require_once 'Nordic/Pagebar/Behaviour.php';
require_once 'Nordic/Pagebar/Element.php';

/**
 * The pagebar is used to create efficiently dynamic navigations
 * with much possibilities to display.
 * 
 * @category   Nordic
 * @package    Nordic_Pagebar
 * @copyright  2007 Nordic Development
 * @license    http://license.nordic-dev.de/newbsd.txt (New-BSD license)
 * @author     Andre Moelle <andre.moelle@gmail.com>
 * @version    $Id: Pagebar.php 3 2007-07-08 09:21:42Z andre.moelle $
 */
class Nordic_Pagebar implements Iterator
{
	/**
	 * Active behaviour.
	 * 
	 * @var Nordic_Pagebar_Behaviour
	 */
	protected $_behaviour;
	
	/**
	 * Default behaviour.
	 * 
	 * @var Nordic_Pagebar_Behaviour
	 */
	protected static $_defaultBehaviour;
	
	/**
	 * Elements in the pagebar.
	 * 
	 * @var array
	 */
	protected $_elements = array();
	
	/**
	 * Index which is used for the iterator.
	 * 
	 * @var integer
	 */
	protected $_index = 0;
	
	/**
	 * Active page.
	 * 
	 * @var integer
	 */
	protected $_page;
	
	/**
	 * Default-page, usually this is 1.
	 * 
	 * @var integer
	 */
	protected $_pageDefault = 1;
	
	/**
	 * Radius determines how many numbers around the active page are
	 * listed.
	 * 
	 * @var integer
	 */
	protected $_radius;
	
	/**
	 * Default radius if no one was specified.
	 * 
	 * @var integer
	 */
	protected $_radiusDefault = 2;
	
	/**
	 * Determines the number of entries displayed on one page.
	 * 
	 * @var integer
	 */
	protected $_result;
	
	/**
	 * Default number, if no integer was given in the constructor.
	 * 
	 * @var integer
	 */
	protected $_resultDefault = 10;
	
	/**
	 * Total number of entries.
	 * 
	 * @var integer
	 */
	protected $_total;
	
	/**
	 * Constructor.
	 * 
	 * Makes the object ready.
	 * 
	 * @param integer $total total number of entries
	 * @param integer $page optional active page
	 * @param integer $radius optional radius
	 * @param integer $results options number determining the entries displayed on one page
	 * @return void
	 */
	public function __construct ($total,
								 $page = null,
								 $radius = null,
								 $results = null,
								 $behaviour = null)
	{
		$this->_total = $total;
		
		if($page == null)
		{
			$page = $this->_pageDefault;
		}
		if($radius == null)
		{
			$radius = $this->_radiusDefault;
		}
		if($results == null)
		{
			$results = $this->_resultDefault;
		}
		
		$this->_radius = ($radius < 0 ? $this->_radiusDefault : $radius);
		$this->_result = ($results < 1 ? $this->_resultDefault : $results);
		$this->_page = $this->_validatePage($page);
		
		if($behaviour != null)
		{
			$this->setBehaviour($behaviour);
		}
	}
	
	/**
	 * Prepares the pagebar before outputting.
	 * 
	 * If the elements-array is already filled and the method is not
	 * forced to prepare, this method is interrupted at the begin.
	 * 
	 * @param boolean $force forces the method to prepare
	 * @return void
	 */
	protected function _prepare ($force = false)
	{
		if(!$force && $this->_elements)
		{
			return;
		}
		
		$this->_elements = array();
		
		$pages = $this->getPages();
		$behaviour = $this->getBehaviour();
		
		for($i = 1; $i <= $pages; $i++)
		{
			$value = $behaviour->applies($this, $i);
			
			if(!$value)
			{
				continue;
			}
			
			$this->_elements[] = new Nordic_Pagebar_Element($behaviour, $i, $value);
		}
	}
	
	/**
	 * Validates the page and returns if necessary the correct number.
	 * 
	 * If the page is less than 1 it defaults to 1. When the value
	 * is greater than the total count of pages, the value defaults
	 * to the last page. Otherwise the given page is returned as
	 * given (but integer-casted).
	 * 
	 * @param integer $page perhaps invalid page
	 * @param integer $pages optional number of total pages
	 * @return integer
	 */
	protected function _validatePage ($page, $pages = null)
	{
		if($pages == null)
		{
			$pages = $this->getPages();
		}
		
		$page = (int)$page;
		
		if($page > $pages)
		{
			return $pages;
		}
		else if($page < 1)
		{
			return 1;
		}
		
		return $page;
	}
	
	/**
	 * The behaviour now comes from the getDefaultBehaviour-method.
	 * 
	 * @param void
	 * @return void
	 */
	public function clearBehaviour ()
	{
		$this->_behaviour = null;
	}
	
	/**
	 * Returns the number of elements.
	 * 
	 * @param void
	 * @return integer
	 */
	public function countElements ()
	{
		$this->_prepare();
		
		return count($this->_elements);
	}
	
	/**
	 * @see Iterator::current()
	 */
	public function current ()
	{
		$this->_prepare();
		
		return $this->_elements[$this->key()];
	}
	
	/**
	 * Returns an instance of a "Nordic_Pagebar"-class.
	 * 
	 * @see Nordic_Pagebar::__construct()
	 */
	public static function factory ($total,
									$page = null,
									$radius = null,
									$results = null,
									$behaviour = null)
	{
		return new Nordic_Pagebar($total, $page, $radius, $results, $behaviour);
	}
	
	/**
	 * Returns the first element which contains the integer.
	 * 
	 * @param integer $integer integer which the element should contain
	 * @param boolean $strict the integer has to be equal to the element's type
	 * @return Nordic_Pagebar_Element|null
	 */
	public function find ($integer, $strict = false)
	{
		$this->_prepare();
		
		foreach($this->_elements as $element)
		{
			if($element->is($integer, $strict))
			{
				return $element;
			}
		}
		
		return null;
	}
	
	/**
	 * Returns the behaviour.
	 * 
	 * @param void
	 * @return Nordic_Pagebar_Behaviour
	 */
	public function getBehaviour ()
	{
		if($this->_behaviour != null)
		{
			return $this->_behaviour;
		}
		
		return self::getDefaultBehaviour();
	}
	
	/**
	 * Returns the default behaviour,
	 * 
	 * @param void
	 * @return Nordic_Pagebar_Behaviour
	 */
	public static function getDefaultBehaviour ()
	{
		if(self::$_defaultBehaviour == null)
		{
			foreach(glob(dirname(__FILE__) . DS . 'Pagebar' . DS . 'Behaviour' . DS . '*.php') as $path)
			{
				$class = __CLASS__ . '_Behaviour_' . basename($path, '.php');
				
				Zend_Loader::loadClass($class);
				$new = new $class();
				
				if(!isset($behaviour))
				{
					$behaviour = $new;
				}
				else
				{
					$new->behave($behaviour);
					$behaviour = $new;
				}
			}
			
			$behaviour->setExponent();
			
			self::setDefaultBehaviour($behaviour);
		}
		
		return self::$_defaultBehaviour;
	}
	
	/**
	 * @see Nordic_Pagebar::_page
	 */
	public function getPage ()
	{
		return $this->_page;
	}
	
	/**
	 * Returns the number of pages.
	 * 
	 * This number is calculated using the total number of entries
	 * and the entries which should be displayed on one page.
	 * 
	 * @param void
	 * @return integer
	 */
	public function getPages ()
	{
		return ceil($this->getTotal() / $this->getResultsPerPage());
	}
	
	/**
	 * @see Nordic_Pagebar::_radius
	 */
	public function getRadius ()
	{
		return $this->_radius;
	}
	
	/**
	 * @see Nordic_Pagebar::_result
	 */
	public function getResultsPerPage ()
	{
		return $this->_result;
	}
	
	/**
	 * @see Nordic_Pagebar::_total
	 */
	public function getTotal ()
	{
		return $this->_total;
	}
	
	/**
	 * @see Iterator::key()
	 */
	public function key ()
	{
		return $this->_index;
	}
	
	/**
	 * @see Iterator::next()
	 */
	public function next ()
	{
		return ++$this->_index;
	}
	
	/**
	 * @see Iterator::rewind()
	 */
	public function rewind ()
	{
		$this->_index = 0;
	}
	
	/**
	 * Sets a new behaviour and creates the pagebar.
	 * 
	 * @param Nordic_Pagebar_Behaviour $behaviour new behaviour
	 * @return void
	 */
	public function setBehaviour (Nordic_Pagebar_Behaviour $behaviour)
	{
		$this->_behaviour = $behaviour;
		$this->_elements = array();
	}
	
	/**
	 * Returns the default behaviour.
	 * 
	 * @param Nordic_Pagebar_Behaviour $behaviour
	 * @return void
	 */
	public static function setDefaultBehaviour (Nordic_Pagebar_Behaviour $behaviour)
	{
		self::$_defaultBehaviour = $behaviour;
	}
	
	/**
	 * @see Iterator::valid()
	 */
	public function valid ()
	{
		$this->_prepare();
		
		return (isset($this->_elements[$this->key()]));
	}
}
?>